package com.wl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import com.alibaba.druid.util.StringUtils;
import com.arcsoft.face.*;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.enums.ErrorInfo;
import com.arcsoft.face.enums.ImageFormat;
import com.arcsoft.face.toolkit.ImageInfo;
import com.wl.constant.Constant;
import com.wl.mapper.UsersMapper;
import com.wl.pojo.UserFace;
import com.wl.pojo.Users;
import com.wl.pojo.model.UserToken;
import com.wl.service.UsersService;
import com.wl.util.*;
import lombok.SneakyThrows;
import org.jose4j.lang.JoseException;
import redis.clients.jedis.Jedis;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.arcsoft.face.toolkit.ImageFactory.getRGBData;

/**
 * @program: treeHole
 * @author: 吴磊
 * @create: 2021-07-06 18:17
 */
public class UsersServiceImpl implements UsersService {

    Myproperties mp = Myproperties.getMyProperties();
    FaceEngine faceEngine;

    {
        String osName = System.getProperty("os.name");
        if (osName.toLowerCase().startsWith("win")) {
            faceEngine = new FaceEngine(mp.getProperty("faceEnginePath.win"));
        } else {
            faceEngine = new FaceEngine(mp.getProperty("faceEnginePath.linux"));
        }
        // 引擎配置
        EngineConfiguration engineConfiguration = new EngineConfiguration();

        //检测模式 图片/视频，这里选择的是图片模式
        engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);

        //人脸检测角度
        engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);

        // 功能配置
        FunctionConfiguration functionConfiguration = new FunctionConfiguration();
        //是否支持年龄检测
        functionConfiguration.setSupportAge(true);
        //是否支持3D人脸检测
        functionConfiguration.setSupportFace3dAngle(true);
        //是否支持人脸检测
        functionConfiguration.setSupportFaceDetect(true);
        //是否支持人脸识别
        functionConfiguration.setSupportFaceRecognition(true);
        //是否支持性别检测
        functionConfiguration.setSupportGender(true);
        //是否支持RGB活体检测
        functionConfiguration.setSupportLiveness(true);
        //是否支持RGB活体检测
        functionConfiguration.setSupportIRLiveness(true);

        //设置引擎功能配置
        engineConfiguration.setFunctionConfiguration(functionConfiguration);

        // 初始化引擎
        int initCode = faceEngine.init(engineConfiguration);

        if (initCode != ErrorInfo.MOK.getValue()) {
            System.out.println("初始化引擎失败");
        }
    }

    private UsersMapper usersMapper = MybatisUtils.getMapper(UsersMapper.class);

    @Override
    public boolean checkUserIsOwnerFace(HttpServletRequest request) {
        String userId = request.getParameter("userId");
        String byUserId = usersMapper.getUserFaceIdByUserId(userId);
        if (byUserId == null) {
            return false;
        }
        return true;
    }

    @SneakyThrows
    @Override
    public Map<String, Object> loginFaceOp(HttpServletRequest request) {
        String base64 = request.getParameter("base64");
        byte[] bytes = Base64.decode(base64);
        byte[] extract = extract(bytes, faceEngine);
        if (extract == null) {
            return null;
        }
        List<UserFace> allUserFace = usersMapper.getAllUserFace();
        for (UserFace user : allUserFace) {
            //如果大于0.8就表示是同一人
            if (contrast(extract, user.getFace(), faceEngine) > 0.8) {
                String userId = user.getUserId();
                UserToken userTokenById = usersMapper.getUserTokenById(userId);
                HashMap<String, Object> map = new HashMap<>();
                map.put("loginUser", userTokenById);
                String token = TokenUtils.generateToken(userTokenById);
                map.put("token", token);
                Jedis jedis = RedisUtil.getJedis();
                jedis.setex(token, 60L * 60 * 24, user.getUserId());
                jedis.close();
                return map;
            }
        }
        return null;
    }

    @Override
    public int addFaceOp(HttpServletRequest request) {
        String userId = request.getParameter("userId");
        if (userId == null) {
            userId = TokenUtils.getUser(request).getUserId();
        }
        String base64 = request.getParameter("base64");
        String userIdOld = usersMapper.getUserFaceIdByUserId(userId);
        byte[] bytes = Base64.decode(base64);
        byte[] extract = extract(bytes, faceEngine);
        List<UserFace> allUserFace = usersMapper.getAllUserFace();
        if (allUserFace != null) {
            for (UserFace userFace : allUserFace) {
                float contrast = contrast(extract, userFace.getFace(), faceEngine);
                if (contrast > 0.8) {
                    if (userFace.getUserId().equals(userId)) {
                        return 2;
                    }
                    return 3;
                }
            }
        }
        if (extract == null) {
            return -1;
        }
        UserFace userFace = new UserFace(userId, extract);
        Integer i = 0;
        if (StringUtils.isEmpty(userIdOld)) {
            i = usersMapper.insertFace(userFace);
            if (i > 0) {
                return 1;
            }
        } else {
            i = usersMapper.updateFace(userFace);
            if (i > 0) {
                return 2;
            }
        }
        return -1;
    }

    /**
     * 人脸特征对比
     *
     * @param face1
     * @param face2
     * @param faceEngine
     * @return
     */
    private float contrast(byte[] face1, byte[] face2, FaceEngine faceEngine) {
        //特征比对
        FaceFeature targetFaceFeature = new FaceFeature();
        targetFaceFeature.setFeatureData(face1);
        FaceFeature sourceFaceFeature = new FaceFeature();
        sourceFaceFeature.setFeatureData(face2);
        FaceSimilar faceSimilar = new FaceSimilar();
        int compareCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
        //返回相似度，0-1之间
        return faceSimilar.getScore();
    }

    /**
     * 人脸特征提取
     *
     * @param file
     * @param faceEngine
     * @return
     */
    private byte[] extract(byte[] file, FaceEngine faceEngine) {
        //人脸检测
        ImageInfo imageInfo = getRGBData(file);
        List<FaceInfo> faceInfoList = new ArrayList<>();
        int detectCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList);
        if (faceInfoList == null || faceInfoList.size() == 0) {
            return null;
        }
        //人脸属性检测
        FunctionConfiguration configuration = new FunctionConfiguration();
        configuration.setSupportAge(true);
        configuration.setSupportFace3dAngle(true);
        configuration.setSupportGender(true);
        configuration.setSupportLiveness(true);
        int processCode = faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList, configuration);
        //3D信息检测
        List<Face3DAngle> face3DAngleList = new ArrayList<>();
        int face3dCode = faceEngine.getFace3DAngle(face3DAngleList);
        if (face3DAngleList == null || face3DAngleList.size() == 0) {
            return null;
        }
//        System.out.println("3D角度：" + face3DAngleList.get(0).getPitch() + "," + face3DAngleList.get(0).getRoll() + "," + face3DAngleList.get(0).getYaw());

        //活体检测
        List<LivenessInfo> livenessInfoList = new ArrayList<>();
        int livenessCode = faceEngine.getLiveness(livenessInfoList);
//        System.out.println("活体：" + livenessInfoList.get(0).getLiveness());
        if (livenessInfoList.get(0).getLiveness() != 1) {
            return null;
        }
        //特征提取
        FaceFeature faceFeature = new FaceFeature();
        int extractCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList.get(0), faceFeature);
        //返回特征值
        return faceFeature.getFeatureData();
    }

    @Override
    public boolean deleteUsersById(Users users) {
        String userId = users.getUserId();
        int i = usersMapper.deleteUser(userId);
        if (i <= 0) {
            return false;
        }
        return true;
    }

    @Override
    public Map<String, Object> getAllUserBySome(HttpServletRequest request) {
        String page = request.getParameter("curPage");
        String role = request.getParameter("role");
        if (role == null) {
            role = "";
        }
        int curPage = 1;
        if (page != null && Integer.parseInt(page) > 0) {
            curPage = Integer.parseInt(page);
        }
        Integer allUserCount = usersMapper.getAllUserCount(role);
        int start = (curPage - 1) * 10;
        Map<String, Object> map = new HashMap<>();
        map.put("role", role);
        map.put("start", start);
        List<Users> UserList = usersMapper.getAllUser(map);
        Map<String, Object> resmap = new HashMap<>();
        if (UserList == null || UserList.size() <= 0) {
            return resmap;
        }
        resmap.put("user", UserList);
        resmap.put("total", allUserCount);
        return resmap;
    }

    @Override
    public Map<String, Object> getAdmin(HttpServletRequest request) {
        HttpSession session = request.getSession();
        Map<String, Object> map = new HashMap<>();
        if (session.getAttribute(Constant.ADMIN) == null) {
            return map;
        }
        Users users = (Users) session.getAttribute(Constant.ADMIN);
        map.put("user", users);
        return map;
    }

    @Override
    public boolean adminLoginOut(HttpServletRequest request) {
        HttpSession session = request.getSession();
        if (session.getAttribute(Constant.ADMIN) == null) {
            return false;
        }
        session.removeAttribute(Constant.ADMIN);
        return true;
    }

    @Override
    public Map<String, Object> adminLogin(HttpServletRequest request) {
        String userName = request.getParameter("userName");
        String passWord = request.getParameter("passWord");
        Users users = usersMapper.adminLogin(userName, passWord);
        Map<String, Object> map = new HashMap<>();
        if (users == null) {
            return map;
        }
        map.put("user", users);
        HttpSession session = request.getSession();
        session.setAttribute(Constant.ADMIN, users);
        return map;
    }

    @Override
    public boolean checkUserPwdOp(HttpServletRequest request) {
        String password = request.getParameter("password");
        UserToken loginUser = TokenUtils.getUser(request);
        String userId = loginUser.getUserId();
        String userPwdByUserId = usersMapper.getUserPwdByUserId(userId);
        if (userPwdByUserId.equals(password)) {
            return true;
        }
        return false;
    }

    @Override
    public Map<String, Object> login(String userTelPhoneOrEmail, String userPassword) throws JoseException, UnsupportedEncodingException {
        Users user = usersMapper.getUserByUserTelPhoneAndPwd(userTelPhoneOrEmail, userPassword);
        HashMap<String, Object> map = new HashMap<>();
        if (user == null) {
            map.put("isLogin", false);
        } else {
            UserToken userToken = new UserToken();
            BeanUtil.copyProperties(user, userToken);
            map.put("isLogin", true);
            map.put("loginUser", userToken);
            String token = TokenUtils.generateToken(userToken);
            Jedis jedis = RedisUtil.getJedis();
            jedis.setex(token, 60L * 60 * 24, user.getUserId());
            map.put("token", token);
            jedis.close();
        }
        return map;
    }

    @SneakyThrows
    @Override
    public Map<String, Object> register(Users users) {
        String userId = MyUtil.getUUID();
        users.setUserName("萌新" + userId.substring(0, 5));
        String currentDetailedTime = MyUtil.getCurrentDetailedTime();
        users.setUserId(userId);
        users.setUserRegistrationTime(currentDetailedTime);
        Integer i = usersMapper.insertUser(users);
        HashMap<String, Object> map = new HashMap<>();
        if (i > 0) {
            Users user = usersMapper.getUserById(users.getUserId());
            UserToken userToken = new UserToken();
            BeanUtil.copyProperties(user, userToken);
            map.put("isLogin", true);
            map.put("loginUser", userToken);
            String token = TokenUtils.generateToken(userToken);
            Jedis jedis = RedisUtil.getJedis();
            jedis.setex(token, 60L * 60 * 24, user.getUserId());
            map.put("token", token);
            jedis.close();
            return map;
        }
        return null;
    }

    @Override
    public boolean update(Users users) {

        Integer i = usersMapper.updateUser(users);
        if (i > 0) {
            return true;
        }
        return false;
    }

    @SneakyThrows
    @Override
    public void loginOut(HttpServletRequest request) {
        String token = TokenUtils.parseTokenToken(request);
        Jedis jedis = RedisUtil.getJedis();
        jedis.del(token);
        jedis.close();
    }

    @Override
    public boolean checkTelIsOkOp(String userTelephoneNumber) {
        Users user = usersMapper.getUserByUserTelPhoneOrUserEmail(userTelephoneNumber);
        return user == null;
    }

    @Override
    public Users getUserById(String userId) {
        Users user = usersMapper.getUserById(userId);
        return user;
    }
}
